package com.xbongbong.saas.model.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.OtherExpenseSheetTypeEnum;
import com.xbongbong.paas.enums.OtherIncomeSheetTypeEnum;
import com.xbongbong.paas.enums.PaySheetTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.help.DistributorMarkHelp;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.redis.RedisTimeConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.pro.dictionary.pojo.DictionaryPojo;
import com.xbongbong.pro.dictionary.pojo.dto.DictionaryListDTO;
import com.xbongbong.pro.dictionary.pojo.dto.DictionarySearchDTO;
import com.xbongbong.pro.dictionary.pojo.vo.DictionaryVO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.domain.dao.DataDictionaryDao;
import com.xbongbong.saas.domain.entity.DataDictionaryEntity;
import com.xbongbong.saas.enums.ShowTypeEnum;
import com.xbongbong.saas.enums.dictionary.ContractStatusEnum;
import com.xbongbong.saas.enums.dictionary.DictionaryEnum;
import com.xbongbong.saas.enums.dictionary.DictionaryTermEnum;
import com.xbongbong.saas.model.DataDictionaryModel;
import com.xbongbong.saas.model.FormDataDictionaryModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author haibin.zhang
 * @version v1.0
 * @date 2019/1/31 9:04
 * @since v1.0
 */
@Service("dictionaryModel")
public class DataDictionaryModelImpl implements DataDictionaryModel {
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private DataDictionaryDao dataDictionaryDao;
    @Resource
    private FormDataDictionaryModel formDataDictionaryModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private DistributorMarkHelp distributorMarkHelp;


    private static final Logger LOG = LoggerFactory.getLogger(DataDictionaryModelImpl.class);


    @Override
    public DataDictionaryEntity getByType(Integer type, String corpid) {
        return dataDictionaryDao.getByType(type,corpid);
    }

    @Override
    public Integer update(Long id, String corpid, String data) {
        return dataDictionaryDao.update(id,corpid,data);
    }

    @Override
    public DataDictionaryEntity getDefaultByType(Integer type) {
        return dataDictionaryDao.getDefaultByType(type);
    }

    @Override
    public Integer insert(DataDictionaryEntity entity) throws XbbException {
        return dataDictionaryDao.insert(entity);
    }

    @Override
    @Deprecated
    public Integer update(DataDictionaryEntity entity) throws XbbException {
        return null;
    }

    @Override
    public List<DataDictionaryEntity> findEntitys(Map<String, Object> param) {
        return dataDictionaryDao.findEntitys(param);
    }

    @Override
    public void updateBatchText(List<DataDictionaryEntity> dataDictionaryEntityList, String corpid) {
        dataDictionaryDao.updateBatchText(dataDictionaryEntityList, corpid);
    }

    @Override
    public Integer getEntitysCount(Map<String, Object> param) {
        return null;
    }

    @Override
    public List findEntitysWithoutSub(Map<String, Object> param) {
        return null;
    }

    @Override
    public DataDictionaryEntity getByKey(Long id, String corpid) {
        return dataDictionaryDao.getByKey(id,corpid);
    }

    //------------以下方法从DictionaryServiceImpl内移入，后续多个项目公用的数据字典方法都降到model层

    @Override
    public DictionaryVO list(DictionaryListDTO dictionaryListDTO) throws XbbException {
        DataDictionaryEntity dataDictionaryEntity = getByType(dictionaryListDTO.getType(),dictionaryListDTO.getCorpid());
        DictionaryVO dictionaryVO = new DictionaryVO();
        List<DictionaryPojo> list;
        try {
            if (dataDictionaryEntity != null) {
                list = JSONArray.parseArray(dataDictionaryEntity.getData(),DictionaryPojo.class);
            } else {
                list = new ArrayList<>();
            }
        }catch (Exception e){
            LOG.error("dictionaryList error",e);
            list = new ArrayList<>();
        }
        if (list.isEmpty()){
            //初始化
            dataDictionaryEntity = getDefaultByType(dictionaryListDTO.getType());
            dataDictionaryEntity.setId(null);
            dataDictionaryEntity.setCorpid(dictionaryListDTO.getCorpid());
            dataDictionaryEntity.setAddTime(DateTimeUtil.getInt());
            dataDictionaryEntity.setUpdateTime(DateTimeUtil.getInt());
            Integer initNot = dictionaryListDTO.getInitNot();
            if (initNot == null || initNot == 0) {
                insert(dataDictionaryEntity);
            }
            list = JSONArray.parseArray(dataDictionaryEntity.getData(),DictionaryPojo.class);
        }
        dictionaryVO.setId(dataDictionaryEntity != null ? dataDictionaryEntity.getId() : null);
        dictionaryVO.setList(list);
        return dictionaryVO;
    }

    @Override
    public List<ItemPoJo> getArrayList(DictionaryEnum dictionaryEnum, String corpid, Long formId) throws XbbException {
        //只获取启用的，且不限制addtionnalField
        DictionarySearchDTO searchDTO = getDictionarySearchDTO(dictionaryEnum, corpid, null, 1, formId);
        Map<String,String> map = getDictionaryMap(searchDTO);
        List<ItemPoJo> itemPoJoList = new ArrayList<>();
        for (Map.Entry<String,String> entry : map.entrySet()){
            ItemPoJo itemPoJo = new ItemPoJo();
            itemPoJo.setValue(entry.getKey());
            itemPoJo.setText(entry.getValue());
            itemPoJoList.add(itemPoJo);
        }
        return itemPoJoList;
    }

    @Override
    public String getDictionaryValue(String corpid, DictionaryEnum dictionaryEnum, Object key) throws XbbException {
        if (dictionaryEnum == null || Objects.isNull(key)) {
            return "";
        }
        // 只获取启用的，且不限制additionalField
        DictionarySearchDTO searchDTO = getDictionarySearchDTO(dictionaryEnum, corpid, null, 1, null);
        Map<String, String> dictionaryMap = getDictionaryMap(searchDTO);
        String value = dictionaryMap.get(Objects.toString(key));
        return Objects.toString(value, "");
    }

    @Override
    public List<ItemPoJo> getDictionaryList4Attr(String saasAttr, String corpid, Long formId) throws XbbException {

        String redisKey = String.format(RedisConstant.DICTIONARY_LIST_FOR_ATTR, corpid, formId, saasAttr);
        String value = paasRedisHelper.getValue(RedisPrefixConstant.SAAS_DICTIONARY, redisKey);
        List<ItemPoJo> itemPoJoList;
        try {
            itemPoJoList = JSONArray.parseArray(value, ItemPoJo.class);
        } catch (Exception e) {
            itemPoJoList = null;
        }
        if (itemPoJoList == null) {
            PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(formId, corpid);
            if (paasFormExplainEntity == null) {
                //实体不存在，直接返回空list，不throw出去
                return new ArrayList<>();
            }
            List<FieldAttrEntity> explainList = JSONArray.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
            for (FieldAttrEntity entity : explainList) {
                if (!Objects.equals(entity.getFieldType(), FieldTypeEnum.COMBO.getType())) {
                    //跳过非下拉
                    continue;
                }
                boolean flag = Objects.equals(entity.getSaasAttr(), saasAttr) || Objects.equals(entity.getAttr(), saasAttr);
                if (flag) {
                    itemPoJoList = entity.getItems();
                    paasRedisHelper.setValue(RedisPrefixConstant.SAAS_DICTIONARY, redisKey, itemPoJoList, TimeConstant.TEN_MINUTE_DURATION);
                    break;
                }
            }
        }
        return itemPoJoList;
    }

    @Override
    public List<ItemPoJo> getDictionaryList4Saas(DictionaryEnum dictionaryEnum, String corpid, Long formId) throws XbbException {
        List<ItemPoJo> itemPoJoList = new ArrayList<>();
        if (dictionaryEnum == null || DictionaryEnum.UNKNOWN == dictionaryEnum) {
            return itemPoJoList;
        }
        DictionaryTermEnum termEnum = dictionaryEnum.getDictionaryTermEnum();
        switch (termEnum){
            case DATA_DICTIONARY_TERM:
            case FORM_DATA_DICTIONARY_TERM:
                //getArrayList方法内部已经路由到不同的查询list去查询字典，公司级别的不会限制表单id
                return getArrayList(dictionaryEnum, corpid, formId);
            case PAAS_FORM_EXPLAIN_TERM:
                return getDictionaryList4Attr(dictionaryEnum.getSaasAttr(), corpid, formId);
            default:
                return itemPoJoList;
        }
    }

    @Override
    public List<String> statisticTypeList(DictionaryEnum dictionaryEnum, String corpid, Long formId) throws XbbException {
        //获取启用，且addtionnalField = 1
        DictionarySearchDTO searchDTO = getDictionarySearchDTO(dictionaryEnum, corpid, 1, 1, formId);
        //统计只获取字典，不进行初始化（即在获取不到公司字典时，会获取corpid=0的数据，但不会把corpid=0的数据初始化入自己公司）
        searchDTO.setInitNot(BasicConstant.ONE);
        Map<String, String> map = getDictionaryMap(searchDTO);
        List<String> statusIn = new ArrayList<>();
        for (Map.Entry<String,String> entry : map.entrySet()){
            String code = entry.getKey();
            // TODO: 2019/6/17 由于底层数据字典缓存机制的变更，此处通过getDictionaryMap方法读取不到字典的addtionnalField，即无法判断是否参与统计。所以暂时特殊处理：写死排除合同的中止状态（目前只有统计时获取合同状态会调用该方法）
            if (ContractStatusEnum.TERMINATION.getCode().equals(code)) {
                continue;
            }
            statusIn.add(code);
        }
        return statusIn;
    }

    @Override
    public Map<String, String> getDictionaryMap(DictionaryEnum dictionaryEnum, String corpid, Long formId) throws XbbException {
        DictionarySearchDTO searchDTO = getDictionarySearchDTO(dictionaryEnum, corpid, null, 1, formId);
        return getDictionaryMap(searchDTO);
    }
    /**
     * 获取原saas内数据字典list，并构建字典code和字典值的对应关系
     * @param dictionaryEnum 数据字典枚举
     * @param corpid 公司id
     * @param formId 表单id，公司级别的非必传，如客户状态、合同状态无需传该值；表单级别和下拉类型字段则必传
     * @return java.util.Map<java.lang.String,java.lang.String>
     * @author zcp
     * @date 2019/5/2 20:37
     * @since v1.0
     * @version v1.0
     */
    @Override
    public Map<String, String> getDictionaryMap4Saas(DictionaryEnum dictionaryEnum, String corpid, Long formId) throws XbbException {
        //获取数据字典list
        List<ItemPoJo> itemPojoList = getDictionaryList4Saas(dictionaryEnum, corpid, formId);
        //用LinkedHashMap保证排序
        Map<String, String> dictionaryMap = new LinkedHashMap<>();
        if (itemPojoList == null) {
            return dictionaryMap;
        }
        for (ItemPoJo itemPojo : itemPojoList) {
            dictionaryMap.put(itemPojo.getValue().toString(), itemPojo.getText());
        }
        return dictionaryMap;
    }

    //------------以下为私有方法
    /**
     * 获取数据字典查询DTO
     * @param dictionaryEnum 数据字典类型
     * @param corpid 公司id
     * @param addtionnalField 是否参与统计
     * @param enable 是否启用
     * @param formId 表单id，不需要时传null
     * @return com.xbongbong.pro.dictionary.pojo.dto.DictionarySearchDTO 数据字段查询dto
     * @author zcp
     * @date 2019/2/24 10:11
     * @since v1.0
     * @version v1.0
     */
    private DictionarySearchDTO getDictionarySearchDTO(DictionaryEnum dictionaryEnum, String corpid, Integer addtionnalField, Integer enable, Long formId) throws XbbException{
        DictionarySearchDTO searchDTO = new DictionarySearchDTO();
        searchDTO.setCorpid(corpid);
        searchDTO.setFormId(formId);
        searchDTO.setDictionaryEnum(dictionaryEnum);
        searchDTO.setEnable(enable);
        searchDTO.setAddtionnalField(addtionnalField);
        if (Objects.nonNull(formId) && Objects.nonNull(corpid)){
            Integer distributorMark = distributorMarkHelp.getDistributorMark(formId, corpid);
            searchDTO.setDistributorMark(distributorMark);
        }
        return searchDTO;
    }

    /**
     * 获取数据字典map---从DictionaryServiceImpl迁移过来
     * @param searchDTO 查询参数(enable和addtionnalField可以为空)
     * @return 数据字典map
     */
    private Map<String,String> getDictionaryMap(DictionarySearchDTO searchDTO) throws XbbException{
        Map<String,String> dictionaryMap = new LinkedHashMap<>();
        DictionaryEnum dictionaryEnum = searchDTO.getDictionaryEnum();
        if (dictionaryEnum == null || dictionaryEnum == DictionaryEnum.UNKNOWN) {
            return dictionaryMap;
        }
        Integer type = dictionaryEnum.getType();
        Integer enable = searchDTO.getEnable();
        Long formId = searchDTO.getFormId();
        String redisKey;
        //客户状态、合同状态是全公司的
        if (dictionaryEnum == DictionaryEnum.CUSTOMER_STATUS || dictionaryEnum == DictionaryEnum.CONTRACT_STATUS) {
            formId = null;
            redisKey = String.format(RedisConstant.DICTIONARY_MAP_FORMID_NULL, searchDTO.getCorpid(), type, enable);
        }else {
            redisKey = String.format(RedisConstant.DICTIONARY_MAP_FORMID_NOT_NULL, searchDTO.getCorpid(), formId, type, enable);
        }
        boolean hasEnable = false;
        boolean isEnable = false;
        if (enable != null){
            hasEnable = true;
            isEnable = enable == 1;
        }

        String filePrefix = RedisPrefixConstant.SAAS_FORM_DICTIONARY;
        if (dictionaryEnum.getDictionaryTermEnum() == DictionaryTermEnum.DATA_DICTIONARY_TERM) {
            filePrefix = RedisPrefixConstant.SAAS_DICTIONARY;
        }

        String value = paasRedisHelper.getValue(filePrefix, redisKey);
        try {
            dictionaryMap = JSONObject.parseObject(value,dictionaryMap.getClass());
        }catch (Exception e){
            dictionaryMap = null;
        }
        if (dictionaryMap == null) {
            dictionaryMap = new LinkedHashMap<>();
            DictionaryListDTO dictionaryListDTO = new DictionaryListDTO();
            BeanUtil.copyProperties(searchDTO,dictionaryListDTO);
            dictionaryListDTO.setType(type);
            DictionaryTermEnum dictionaryTermEnum = dictionaryEnum.getDictionaryTermEnum();
            DictionaryVO dictionaryVO;
            switch (dictionaryTermEnum) {
                case DATA_DICTIONARY_TERM:
                    //此处不限制formId
                    dictionaryVO = list(dictionaryListDTO);
                    break;
                case FORM_DATA_DICTIONARY_TERM:
                    //在tb_form_data_dictionary表内存的数据字典，则调用其相应model方法，限制formId
                    dictionaryVO = formDataDictionaryModel.list(dictionaryListDTO);
                    break;
                default:
                    return dictionaryMap;
            }
            List<DictionaryPojo> list = dictionaryVO.getList();
            for (DictionaryPojo pojo : list) {
                if (hasEnable && pojo.getEnable() == 1 ^ isEnable) {
                    continue;
                }
                dictionaryMap.put(pojo.getValue(), pojo.getText());
            }
            paasRedisHelper.setValue(RedisPrefixConstant.SAAS_DICTIONARY, redisKey, dictionaryMap, RedisTimeConstant.TEN_MINUTES);
        }
        return dictionaryMap;
    }

    @Override
    public void updateBatch(String corpid, Map<Long, String> updateMap) {
        dataDictionaryDao.updateBatch(corpid,updateMap);
    }

    @Override
    public void removeRedis(String corpid, Integer type) {
        String redisKey = corpid + "_" + type + "_" + null;
        paasRedisHelper.removeValue(RedisPrefixConstant.SAAS_DICTIONARY,redisKey);
        redisKey = corpid + "_" + type + "_" + 1;
        paasRedisHelper.removeValue(RedisPrefixConstant.SAAS_DICTIONARY,redisKey);
        redisKey = corpid + "_" + type + "_" + 0 ;
        paasRedisHelper.removeValue(RedisPrefixConstant.SAAS_DICTIONARY,redisKey);
    }

    @Override
    public void insertBatch(List<DataDictionaryEntity> list) {
        dataDictionaryDao.insertBatch(list);
    }

    @Override
    public List<ItemPoJo> getDictionaryList4PaymentSheetType(boolean prepaymentMenuFlag) {
        //与预收款相关的回款单类型code
        List<String> prepaymentCodeList = PaymentSheetTypeEnum.getPrepaymentCodeList();
        List<ItemPoJo> list = new ArrayList<>();
        for (PaymentSheetTypeEnum cache : PaymentSheetTypeEnum.values()) {
            String code = cache.getCode();
            if (Objects.equals(code, PaymentSheetTypeEnum.UNKNOWN.getCode())) {
                continue;
            }
            ItemPoJo itemPoJo = new ItemPoJo();
            //预收款没开启，则把预收款相关类型置为任何地方都不显示
            boolean flag = (!prepaymentMenuFlag && prepaymentCodeList.contains(code));
            if (flag) {
                itemPoJo.setShowType(ShowTypeEnum.NONE.getCode());
            }
            itemPoJo.setValue(code);
            itemPoJo.setText(cache.getName());
            itemPoJo.setNoEdit(BasicConstant.ONE);
            itemPoJo.setNoMove(BasicConstant.ONE);
            itemPoJo.setNoDel(BasicConstant.ONE);
            list.add(itemPoJo);
        }
        return list;
    }

    @Override
    public List<ItemPoJo> getDictionaryList4PaySheetType(boolean prepayMenuFlag) {
        //与预付款相关的付款单类型code
        List<String> prepayCodeList = PaySheetTypeEnum.getPrepayCodeList();
        List<ItemPoJo> list = new ArrayList<>();
        for (PaySheetTypeEnum cache : PaySheetTypeEnum.values()) {
            String code = cache.getCode();
            if (Objects.equals(code, PaySheetTypeEnum.UNKNOWN.getCode())) {
                continue;
            }
            ItemPoJo itemPoJo = new ItemPoJo();
            //预付款没开启，则把预付款相关类型置为任何地方都不显示
            boolean flag = (!prepayMenuFlag && prepayCodeList.contains(code)) || Objects.equals(code, PaySheetTypeEnum.UNKNOWN.getCode());
            if (flag) {
                itemPoJo.setShowType(ShowTypeEnum.NONE.getCode());
            }
            itemPoJo.setValue(code);
            itemPoJo.setText(cache.getName());
            itemPoJo.setNoEdit(BasicConstant.ONE);
            itemPoJo.setNoMove(BasicConstant.ONE);
            itemPoJo.setNoDel(BasicConstant.ONE);
            list.add(itemPoJo);
        }
        return list;
    }

    @Override
    public List<ItemPoJo> getDictionaryList4OtherIncomeSheetType() {
        List<ItemPoJo> list = new ArrayList<>();
        for (OtherIncomeSheetTypeEnum cache : OtherIncomeSheetTypeEnum.values()) {
            String code = cache.getCode();
            if (Objects.equals(code, OtherIncomeSheetTypeEnum.UNKNOWN.getCode())) {
                continue;
            }
            ItemPoJo itemPoJo = new ItemPoJo();
            itemPoJo.setValue(code);
            itemPoJo.setText(cache.getName());
            itemPoJo.setNoEdit(BasicConstant.ONE);
            itemPoJo.setNoMove(BasicConstant.ONE);
            itemPoJo.setNoDel(BasicConstant.ONE);
            list.add(itemPoJo);
        }
        return list;
    }

    @Override
    public List<ItemPoJo> getDictionaryList4OtherExpenseSheetType() {
        List<ItemPoJo> list = new ArrayList<>();
        for (OtherExpenseSheetTypeEnum cache : OtherExpenseSheetTypeEnum.values()) {
            String code = cache.getCode();
            if (Objects.equals(code, OtherExpenseSheetTypeEnum.UNKNOWN.getCode())) {
                continue;
            }
            ItemPoJo itemPoJo = new ItemPoJo();
            itemPoJo.setValue(code);
            itemPoJo.setText(cache.getName());
            itemPoJo.setNoEdit(BasicConstant.ONE);
            itemPoJo.setNoMove(BasicConstant.ONE);
            itemPoJo.setNoDel(BasicConstant.ONE);
            list.add(itemPoJo);
        }
        return list;
    }
}
